package client_listen_update_files;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.HttpEntity;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.NameValuePair;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.ContentBody;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import dropbox.common.Utils;

/**
 * this class sends request to server such as: add/update
 * ,delete,download,rename file/dir or check existence of file in server
 * 
 * @author Yoav
 * 
 */
public class IteractionWithServer {

	private String userName;
	private String password;

	// holds server add/update file/dir address
	private String addUpdateAddress;
	// holds server check if file exists address
	private String checkAddress;
	// holds server delete a file/dir address
	private String deleteAddress;
	// holds server download file address
	private String downloadAddress;
	// holds server rename file/dir address
	private String renameAddress;

	/**
	 * constructor
	 * 
	 * @param address server address
	 * @param userName user name
	 * @param password user's password
	 */
	public IteractionWithServer(String address, String userName, String password) {
		this.userName = userName;
		this.password = password;
		this.addUpdateAddress = address + "/AddUpdate";
		this.checkAddress = address + "/ChkExists";
		this.deleteAddress = address + "/Delete";
		this.downloadAddress = address + "/Download";
		this.renameAddress = address + "/Rename";
	}

	/**
	 * download a file from servlet to client
	 * @param checkSum file's checkSum
	 * @param file file at the client side that is ready to get content
	 */
	public void downloadFileFromServlet(String checkSum, File file) {
		/*set http client and http post in order to send through post method
	 	request to server also insert user name and password in header*/
		DefaultHttpClient httpclient = new DefaultHttpClient();
		HttpRequestInterceptor preemtiveAuth = getInterWithUsrNPass();
		httpclient.getParams().setParameter(
				CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
		httpclient.addRequestInterceptor(preemtiveAuth, 0);
		HttpPost httppost = new HttpPost(downloadAddress);

		//add checkSum parameter
		List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
		nameValuePairs.add(new BasicNameValuePair("checkSum", checkSum));
		try {
			httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
			HttpResponse response;
			
			//execute request
			response = httpclient.execute(httppost);

			//get file from servlet
			
			BufferedInputStream in = new BufferedInputStream(response
					.getEntity().getContent());
			BufferedOutputStream out = new BufferedOutputStream(
					new FileOutputStream(file));
			//buffer
			byte[] buffer = new byte[4096];
			//byte read from server
			int byteRead = 0;
			
			//write to file
			while ((in != null) && ((byteRead = in.read(buffer)) != -1)) {
				out.write(buffer, 0, byteRead);
			}
			//close in out streams
			in.close();
			out.close();

			//end connection
			if (response.getEntity() != null) {
				EntityUtils.consume(response.getEntity());
			}
			httpclient.getConnectionManager().shutdown();
		} catch (IOException e) {
		}
	}

	/**
	 * send a file to servlet from client
	 * @param file client's file to be sent to server
	 * @param filePath file's path
	 */
	public void sendFileToServlet(File file, String filePath){
		//check if folder
		if (file.isDirectory()) {
			//no need for checkSum, info only
			sendFile(file, true, filePath);
		} else {
			//file, get checkSum and check if file already on server
			String checkSum = Utils.getCheckSum(file);
			sendFile(file, checkExistsOnServer(checkSum), filePath);
		}

	}

	/**
	 * delete file from servlet
	 * @param filePath file path that was deleted on client's side 
	 */
	public void deleteFileFromServlet(String filePath) {

		/*set http client and http post in order to send through post method
	 	request to server also insert user name and password in header*/
		DefaultHttpClient httpclient = new DefaultHttpClient();
		HttpPost httppost = new HttpPost(deleteAddress);

		HttpRequestInterceptor preemtiveAuth = getInterWithUsrNPass();
		httpclient.getParams().setParameter(
				CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
		httpclient.addRequestInterceptor(preemtiveAuth, 0);
		
		//add file path parameter
		List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
		nameValuePairs.add(new BasicNameValuePair("filePath", filePath));
		try {
			
			httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
			//execute request
			HttpResponse response = httpclient.execute(httppost);
			HttpEntity resEntity = response.getEntity();
			if (resEntity != null) {
				EntityUtils.consume(resEntity);
			}
			httpclient.getConnectionManager().shutdown();

		} catch (UnsupportedEncodingException e) {
		} catch (ClientProtocolException e) {
		} catch (IOException e) {
		}
	}
	/*
	 * checks if file already exists on server 
	 * @param checkSum file's checkSum
	 * @return
	 * @throws ClientProtocolException
	 * @throws IOException
	 */
	private boolean checkExistsOnServer(String checkSum) {
		boolean returnValue = false;
		try {
			
			/*set http client and http post in order to send through post method
		 	request to server also insert user name and password in header*/
			DefaultHttpClient httpclient = new DefaultHttpClient();
			HttpRequestInterceptor preemtiveAuth = getInterWithUsrNPass();
			httpclient.getParams().setParameter(
					CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
			httpclient.addRequestInterceptor(preemtiveAuth, 0);
			List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
			
			//add checkSum parameter
			nameValuePairs.add(new BasicNameValuePair("checkSum", checkSum));
			
			HttpPost httppost = new HttpPost(checkAddress);
			httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
			//execute request
			HttpResponse response = httpclient.execute(httppost);
			
			//get answer from server
			HttpEntity resEntity = response.getEntity();
			String serverAnswer = EntityUtils.toString(resEntity);
			//check if answer from server true
			if (serverAnswer.equals("true")) {
				returnValue = true;
			}
			//close connection
			if (resEntity != null) {
				EntityUtils.consume(resEntity);
			}
			httpclient.getConnectionManager().shutdown();

		} catch (IOException e) {
		}
		return returnValue;
	}
	/*
	 * get interceptor instance ready to use
	 * @param userName user's name
	 * @param password user's password
	 * @return HttpRequestinterceptor ready to use
	 */
	private HttpRequestInterceptor getInterWithUsrNPass() {
		return new HttpRequestInterceptor() {
			@Override
			public void process(HttpRequest req, HttpContext context)
					throws HttpException, IOException {
				req.addHeader(new BasicScheme().authenticate(
						new UsernamePasswordCredentials(userName, password),
						req));
			}
		};
	}

	/*
	 * send a file to servlet
	 * @param file client's file to be sent to server
	 * @param infoOnly indicates if need to send the file
	 * @param path file's path
	 */
	private void sendFile(File file, boolean infoOnly, String path) {
		try {
			/*set http client and http post in order to send through post method
		 	request to server also insert user name and password in header*/
			DefaultHttpClient httpclient = new DefaultHttpClient();
			HttpRequestInterceptor preemtiveAuth = getInterWithUsrNPass();
			httpclient.addRequestInterceptor(preemtiveAuth, 0);
			httpclient.getParams().setParameter(
					CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
			HttpPost httppost = new HttpPost(addUpdateAddress);
			
			MultipartEntity mpEntity = new MultipartEntity();
			ContentBody cbFile = new FileBody(file);
			//add file path parameter
			mpEntity.addPart("filePath", (ContentBody) new StringBody(path));
			//check if need to send file
			if (!infoOnly) {
				//add file parameter
				mpEntity.addPart("file", cbFile);
			} else {
				//no need to send file
				//check if not dir
				if (!file.isDirectory()) {
					//get file's checkSum and add checkSum parameter
					String checkSum = Utils.getCheckSum(file);
					mpEntity.addPart("checkSum",
							new StringBody(String.valueOf(checkSum)));
				}
			}
			httppost.setEntity(mpEntity);
			HttpResponse response;
			//execute request
			response = httpclient.execute(httppost);
			//close connection
			HttpEntity resEntity = response.getEntity();
			if (resEntity != null) {
				EntityUtils.consume(resEntity);
			}
			httpclient.getConnectionManager().shutdown();

		} catch (IOException e) {
		}

	}
	/**
	 * send request to servlet for changing a file/dir name
	 * that was changed on client's side
	 * 
	 * @param oldName old file/dir name
	 * @param newName new file/dir name
	 */
	public void rename(String oldName, String newName) {
		try {
			/*set http client and http post in order to send through post method
		 	request to server also insert user name and password in header*/
			DefaultHttpClient httpclient = new DefaultHttpClient();
			HttpRequestInterceptor preemtiveAuth = getInterWithUsrNPass();
			httpclient.getParams().setParameter(
					CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
			httpclient.addRequestInterceptor(preemtiveAuth, 0);
			List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();

			//add old path and new path parameter
			nameValuePairs.add(new BasicNameValuePair("oldPath", oldName));
			nameValuePairs.add(new BasicNameValuePair("newPath", newName));
			
			HttpPost httppost = new HttpPost(renameAddress);
			httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));

			//execute request
			HttpResponse response = httpclient.execute(httppost);
			HttpEntity resEntity = response.getEntity();
			//close connection
			if (resEntity != null) {
				EntityUtils.consume(resEntity);
			}
			httpclient.getConnectionManager().shutdown();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}
